from pathlib import Path
import sys

sys.path.append(str(Path(__file__).parent.parent))  # add parent dir to sys path

from src.visualize import (
    do_visualize,
    visualize_diff,
    visualize_ADF_result,
    visualize_fast_result,
    visualize_total_result,
)
from src.smooth_data import do_smooth
from src.ADF_test import do_ADF_test
from src.diff_test import do_diff_test
from src.fast_test import do_fast_test
from typing import Dict, List
import json
import os
import numpy as np
import math
import pandas as pd
from datetime import datetime, timedelta

# with open(r"data\pattern.json", "r") as f:
#     pattern = json.load(f)


# def count_order_of_magnitude(num1, num2):
#     ratio = abs(num1 / num2)
#     order_of_magnitude = math.floor(math.log10(ratio))
#     return order_of_magnitude


def count_mode(num_list: pd.Series):
    """计算众数"""
    return float(num_list.mode().iloc[0])


def get_order(num_list: pd.Series, aim_order: int):
    order_list = pd.Series([int(math.log10(abs(number))) for number in num_list.values])
    mode = count_mode(order_list)
    order = aim_order - mode
    return int(order)


def deal_missing_value(signal_data: pd.Series):
    """处理缺失值"""
    signal_data = pd.Series(signal_data).replace(0, pd.NA)
    # if some data is zero, fill it with the mean of the data
    if signal_data.iloc[0] is pd.NA:
        i = 0
        while signal_data.iloc[i] is pd.NA:
            i += 1
        signal_data.iloc[0] = signal_data.iloc[i]

    signal_data = signal_data.ffill()
    return signal_data


def rebuild_index(ts_sub_data: pd.DataFrame, time_interval: int = 4):
    """
    use time_interval to rebuild the index of ts_sub_data,
    input: ts_sub_data, time_interval
    output: ts_sub_data with new index

    """
    timestamps = ts_sub_data["Ts"]
    # 将时间戳转换为datetime对象
    datetime_objects = [
        datetime.strptime(ts, "%Y-%m-%d %H:%M:%S") if isinstance(ts, str) else ts
        for ts in timestamps
    ]

    # 计算每个时间戳与起始时间戳的差值（秒）
    time_diffs = [(ts - datetime_objects[0]).total_seconds() for ts in datetime_objects]

    # 将秒数差值除以4并转换为自然数
    natural_numbers = [float(diff / 4) for diff in time_diffs]
    ts_sub_data["ts_range"] = natural_numbers
    # print(ts_sub_data.head())
    return ts_sub_data


def get_time_interval(signal_data: pd.Series, method: str = "mode"):
    """获取时间间隔"""
    if method == "mode":
        return signal_data.index.to_series().diff().mode().iloc[0]
    elif method == "mean":
        return signal_data.index.to_series().diff().mean()
    else:
        raise ValueError("method must be mode or mean")


def fullfill_missing_value(signal_data: pd.DataFrame, method="ffill") -> pd.Series:
    """buiild a new time index and check the input data, if there is missing value, fill it with method"""

    signal_data = signal_data.fillna(method=method)
    return signal_data


def split_dataframe(df: pd.DataFrame, threshold: pd.Timedelta) -> list:
    split_points = [
        i for i in range(1, len(df)) if df.index[i] - df.index[i - 1] > threshold
    ]

    split_dfs = [
        df.iloc[i:j] for i, j in zip([0] + split_points, split_points + [len(df)])
    ]
    return split_dfs


def prase_signal_data(
    signal_data: pd.DataFrame,
):
    """解析信号数据"""
    time_data = signal_data.index
    if (time_data is not None) and (len(signal_data) != len(time_data)):
        # print("signal len: ", len(signal_data))
        # print("time len: ", len(time_data))
        raise ValueError("signal data length must same as time data length")

    if time_data is None:
        time_data = pd.date_range(
            start="2024-01-01", periods=len(signal_data), freq="4S"
        )
    signal_data = pd.Series(signal_data, index=pd.to_datetime(time_data))

    index_min = signal_data.index.min()
    index_max = signal_data.index.max()

    freq = get_time_interval(signal_data).seconds

    time_index = pd.date_range(start=index_min, end=index_max, freq=f"{freq}s")

    # time_index = pd.date_range(
    #     start="2021-01-01", periods=len(signal_data), freq="1S"
    # )  # 生成时间序列
    time_index
    signal_data = pd.Series(signal_data, index=time_index)

    return signal_data
    # else:
    #     return pd.Series(signal_data, index=pd.to_datetime(time_data))


# 定义一个函数，接受两个列表作为参数，返回将第二个列表转换为和第一个列表相同数量级的新列表
def convert_scale(signal_data, order):
    return signal_data.apply(lambda x: x * (10**order))


def save_json_result(result, save_dir):
    save_dir = Path(save_dir)
    save_dir.parent.mkdir(parents=True, exist_ok=True)
    with open(str(save_dir), "w") as f:
        json.dump(result, f, indent=4, ensure_ascii=False)


def main(input_data: Dict, save_dir="test"):
    """
    main function
    save_dir: save output, if vis, save both json file and jpg file
    """
    # TODO: 应该需要归一化才行,但是参数需要重新弄， 所以后续有时间再搞
    config_data = input_data["config"]
    origin_signal_data = input_data["signal"]
    if "time" in input_data:
        time_data = input_data["time"]
    else:
        time_data = None
    if time_data and len(origin_signal_data) != len(time_data):
        raise ValueError("signal data length must same as time data length")

    SIGMA = config_data["SIGMA"]  # 平滑系数
    ERROR_N = config_data["ERROR_N"]  # 极差标准差系数, 决定机动点
    FIGSIZE = config_data["FIGSIZE"]  # 图片大小
    DEBUG = config_data["DEBUG"]  # 是否调试

    SLOW_STEP = config_data["SLOW_STEP"]  # 慢速步长窗口
    FAST_STEP = config_data["FAST_STEP"]  # 快速步长窗口

    TAN_RANGE = config_data["TAN_RANGE"]  # 斜率范围 用于快速慢速检测
    ST = config_data["ST"]  # 静止阈值 用于地面站静止
    FAST_FREQ_RANGE, SLOW_FREQ_RANGE = (
        config_data["FAST_FREQ_RANGE"],  # 快速频率范围
        config_data["SLOW_FREQ_RANGE"],  # 慢速频率范围
    )
    try:
        CONTINUE_THRESHOLD = config_data["CONTINUE_THRESHOLD"]  # 信号连续阈值
    except:
        # 如未传入信号连续阈值，则取步长的最小值
        CONTINUE_THRESHOLD = min([SLOW_STEP, FAST_STEP])
    CONTINUE_THRESHOLD = str(CONTINUE_THRESHOLD) + "s"  # 以秒为单位
    print("begin")

    # prase signal data

    # 根据时间间隔拆分数据
    if time_data:
        origin_signal_data = pd.Series(
            origin_signal_data, index=pd.to_datetime(time_data)
        )
        signal_data_list = split_dataframe(
            origin_signal_data, pd.Timedelta(CONTINUE_THRESHOLD)
        )

    else:
        signal_data_list = [origin_signal_data]

    fast_res = []
    stable_res = []
    turbulence_res = []
    static_res = []
    # 分块处理结果

    sum_len = 0
    for signal_data in signal_data_list:  # 因为拆分了区域所以需要计算多次
        print(len(signal_data))
        sum_len += len(signal_data)
    print("total block: ", sum_len)

    begin_index = 0
    ts_range_end = 0
    smooth_data_total = pd.DataFrame()
    for signal_data in signal_data_list:  # 因为拆分了区域所以需要计算多次
        # 缺失值处理

        # TODO 长度过短的部分不判？
        if len(signal_data) < min([SLOW_STEP, FAST_STEP]):
            continue
        # print(signal_data.head())
        signal_data = prase_signal_data(signal_data)

        # deal_missing_value
        signal_data = deal_missing_value(signal_data)
        # normalization
        order_diff = get_order(signal_data, aim_order=2)  # 获取数量级

        # data: dict = get_data(output_data_folder)
        signal_data = convert_scale(signal_data, order_diff)
        # 平滑Freq曲线

        smooth_data = do_smooth(signal_data, sigma=SIGMA)
        # print(smooth_data.tail())
        # print(signal_data[-5:])
        # 可视化数据

        # normalize data x rescale
        rebuild_index(smooth_data)
        # print(smooth_data.head())
        ## 机动检测:
        (
            diff_data,
            turbulence_point_result,
            turbulence_point_index,
            upper_bound,
            lower_bound,
        ) = do_diff_test(smooth_data, ERROR_N)

        # stable test
        # stable_test_result, stable_data = do_ADF_test(
        #     smooth_data, SLOW_STEP, SLOW_FREQ_RANGE, ST
        # )
        stable_test_result, stable_data = do_fast_test(
            smooth_data,
            SLOW_STEP,
            upper_rad=TAN_RANGE[0],
            lower_rad=ST,
            Freq_range=FAST_FREQ_RANGE,
        )
        static_test_result, static_data = do_fast_test(
            smooth_data,
            FAST_STEP,
            upper_rad=ST,
            lower_rad=0,
            Freq_range=FAST_FREQ_RANGE,
        )
        # fast test
        fast_test_result, fast_data = do_fast_test(
            smooth_data,
            FAST_STEP,
            upper_rad=TAN_RANGE[1],
            lower_rad=TAN_RANGE[0],
            Freq_range=FAST_FREQ_RANGE,
        )

        # 修正索引
        fast_test_result = [
            (i[0], i[1] + begin_index, i[2] + begin_index) for i in fast_test_result
        ]
        stable_test_result = [
            (i[0], i[1] + begin_index, i[2] + begin_index) for i in stable_test_result
        ]
        static_test_result = [
            (i[0], i[1] + begin_index, i[2] + begin_index) for i in static_test_result
        ]

        # 修正ts_range

        smooth_data["ts_range"] = smooth_data["ts_range"] + ts_range_end
        ts_range_end = smooth_data["ts_range"].iloc[-1]
        # print(fast_test_result[-1])
        # 拼接结果
        fast_res.append(fast_test_result)
        stable_res.append(stable_test_result)
        turbulence_res.append(turbulence_point_result)
        static_res.append(static_test_result)
        smooth_data_total = pd.concat([smooth_data_total, smooth_data])
        begin_index += len(signal_data)
        # 可视化结果
        smooth_data

    visualize_total_result(
        input_data,
        smooth_data_total,
        diff_data,
        turbulence_point_index,
        upper_bound,
        lower_bound,
        stable_res,
        fast_res,
        static_res,
        FAST_STEP,
        SLOW_STEP,
        FAST_FREQ_RANGE,
        SLOW_FREQ_RANGE,
        TAN_RANGE,
        ST,
        DEBUG,
        FIGSIZE,
        save_dir=save_dir,
    )

    result = {
        "config": config_data,
        "stable_test_result": stable_res,  # {"Freq": [(bool, index_start, index_end)]}
        "fast_test_result": fast_res,  # {"Freq": [(bool, index_start, index_end)]}
        "static_test_result": static_test_result,  # {"Freq": [(bool, index_start, index_end)]}
        "turbulence_point_index": turbulence_res,  # {"Freq": [(bool, index)]}
    }

    save_json_result(result, save_dir)

    return result


if __name__ == "__main__":
    """test script"""

    import json
    import os
    import copy

    # pattern
    with open(r"data\json_data\data_2_20.json", "r") as f:
        pattern = json.load(f)
    main(pattern, save_dir=r"main_test\test.json")
    # test_data_dir = r"data\test_data"
    # csv_data_list = [i for i in os.listdir(test_data_dir) if i.endswith(".csv")]

    # for csv_name in csv_data_list:
    #     input_data = {}  # 清空原数据
    #     data = []
    #     print(input_data)
    #     csv_path = os.path.join(test_data_dir, csv_name)
    #     # with open(csv_path, "r") as f:
    #     #     data = f.readlines()

    #     data = pd.read_csv(csv_path, header=None)
    #     input_data = {
    #         "config": pattern["config"],
    #         "signal": data.values.flatten().tolist(),
    #     }
    #     print(f"{csv_name} begin".center(100, "="))

    #     print(len(input_data["signal"]))
    #     main(input_data, save_dir=csv_name[:-4])
